#include "srf.h"
#include "lodepng.h"

SRF::SRF()
{
    tdWidth = tdHeight = topWidth = topHeight = 0;
    tdMask = topMask = tdRGB = topRGB = NULL;
    sprintf(lastErr, "No error");
}

SRF::~SRF()
{
    delete [] tdMask;
	delete [] topMask;
    delete [] tdRGB;
    delete [] topRGB;
}

void SRF::Read(const char* file)
{
    if( !tdMask )
    {
        delete [] tdMask;
        delete [] topMask;
        delete [] tdRGB;
        delete [] topRGB;
    }

    FILE* fSRF = fopen(file, "rb");
    memset(garmin, 0, 32);
    memset(product, 0, 32);

    // "GARMIN BITMAP 01" (not null terminated) followed by 3 ints
    fread(garmin, 1, 16, fSRF);
    fread(&hdrN1, 1, 4, fSRF);
    fread(&hdrN2, 1, 4, fSRF);
    fread(&hdrN3, 1, 4, fSRF);

    // read strings till type 7(?)
    char buf[256];
    int nType;
    int nLen;
    nStringCount = 0;
    do
    {
        fread(&nType, 1, 4, fSRF);
        if( nType != 0 )
        {
            fread(&nLen, 1, 4, fSRF);
            memset(buf, 0, 256);
            if( nLen > 0 )
                fread(buf, 1, nLen, fSRF);
            strings[nStringCount++] = buf;
        }
    }while( nType != 7 );

    fread(&nUnknown1, 1, 4, fSRF);  // unknown int, usually but not always 0

    // next int is size of 3D view info - usually 0x10 or 0x14
    fread(&nSize3DInfo, 1, 4, fSRF);
    fread(tdInfo, 1, nSize3DInfo, fSRF);

    tdHeight = *(short*)&tdInfo[4];  // 2 bytes here are height
    tdWidth  = *(short*)&tdInfo[6];  // total width = width of each image * 36.  Sometimes with some padding

    fread(&nhexB1, 1, 4, fSRF);         // = 0x0b
    fread(&nLen3DMask, 1, 4, fSRF);    // length of data to follow

    tdMask = new unsigned char[nLen3DMask];
    fread(tdMask, nLen3DMask, 1, fSRF);

    fread(&nUnknown2, 1, 4, fSRF);

    fread(&nLen3DRGB, 1, 4, fSRF);
    tdRGB = new unsigned char[nLen3DRGB];
    fread(tdRGB, nLen3DRGB, 1, fSRF);  

    fread(&nUnknown3, 1, 4, fSRF);     // usually 0?

    // next int is size of top view info - usually 0x10 or 0x14
    fread(&nSizeTopInfo, 1, 4, fSRF);
    fread(topInfo, 1, nSizeTopInfo, fSRF);

    topHeight = *(short*)&topInfo[4];  // 2 bytes here are height
    topWidth  = *(short*)&topInfo[6];  // total width = width of each image * 36.  Sometimes with some padding

    fread(&nhexB1, 1, 4, fSRF);         // = 0x0b

    fread(&nLenTopMask, 1, 4, fSRF);    // length of data to follow

    topMask = new unsigned char[nLenTopMask];
    fread(topMask, nLenTopMask, 1, fSRF);
    fread(&nUnknown4, 1, 4, fSRF); // usually 0x01

    fread(&nLenTopRGB, 1, 4, fSRF);
    topRGB = new unsigned char[nLenTopRGB];
    fread(topRGB, nLenTopRGB, 1, fSRF);

    chksum = Checksum(fSRF);

    fclose(fSRF);
}


unsigned char SRF::Checksum(FILE* fSRF)
{
     unsigned char sum = 0;
     unsigned char byte;

    fseek(fSRF, 0, SEEK_SET);
    while( fread(&byte, 1, 1, fSRF) > 0 )
    {
        sum += byte;
    }

    return sum;
}

void SRF::WriteImages(char* strFilePrefix, unsigned char* buf, unsigned char* mask, short widthTotal, short height)
{
    int width = widthTotal / 36;

    std::vector<unsigned char> image;
    image.resize(width * height * 4);

    for(int i=0; i<36; i++)
    {
        char fname[256];
        sprintf(fname, "%s_%d0.png", strFilePrefix, i);

        for(int x = 0; x < height; x++)
        {
            for(int y = 0; y < width; y++)
            {
              unsigned short pixel = *(unsigned short*)&buf[(x*widthTotal*2+i*width*2)+y*2];

              int red = (pixel & 0xf800) >> 11;
              red = (red << 3) | red;

              int green = (pixel & 0x07c0) >> 6;
              green = (green << 3) | green;

              int blue = (pixel & 0x001f);
              blue = (blue << 3) | blue;

              unsigned char alpha = mask[x*widthTotal+i*width+y];
              if( alpha == 0 )
                  alpha = 255;

              if( alpha == 0x80 )
                  alpha = 0;

              image[4 * width * x + 4 * y + 0] = red;
              image[4 * width * x + 4 * y + 1] = green;
              image[4 * width * x + 4 * y + 2] = blue;
              image[4 * width * x + 4 * y + 3] = alpha;
            }
        }

        lodepng::encode(fname, image, width, height);
    }
}

void SRF::Write(const char* strFilePrefix)
{
    if( tdWidth == 0 || tdHeight == 0 || topWidth == 0 || topHeight == 0 )
        return;

    char strFile[256];
    sprintf(strFile, "%s_3D", strFilePrefix);
    WriteImages(strFile, tdRGB, tdMask, tdWidth, tdHeight);

    sprintf(strFile, "%s_Top", strFilePrefix);
    WriteImages(strFile, topRGB, topMask, topWidth, topHeight);
}

void SRF::Create(const char* prefix)
{
    unsigned char fileHDR[] = {0x47, 0x41, 0x52, 0x4d, 0x49, 0x4e, 0x20, 0x42, 0x49, 0x54, 0x4d, 0x41, 0x50, 0x20, 0x30, 0x31,
                           0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
                           0x03, 0x00, 0x00, 0x00, 0x35, 0x37, 0x38, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31,
                           0x2e, 0x30, 0x30, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x30, 0x36, 0x2d, 0x44,
                           0x30, 0x35, 0x37, 0x38, 0x2d, 0x58, 0x58};

    unsigned char maskHDR[] = {0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0xa8, 0x0c,
                               0x10, 0x08, 0x50, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x10, 0x73, 0x04, 0x00};

    char fn[256];
    sprintf(fn, "%s.srf", prefix);
    FILE* fSRF = fopen(fn, "wb+");

    std::vector<unsigned char> image; //the raw pixels
    unsigned width, height;

    sprintf(fn, "%s_3D_00.png", prefix);
    lodepng::decode(image, width, height, fn);

    *(short*)&maskHDR[12] = (short)height;
    *(short*)&maskHDR[14] = (short)(width * 36);
    *(short*)&maskHDR[18] = (short)(width * 36 * 2);
    *(int*)&maskHDR[28] = (int)(width * height * 36);

    fwrite(fileHDR, 1, sizeof(fileHDR), fSRF);
    fwrite(maskHDR, 1, sizeof(maskHDR), fSRF);

    char baseFile[256];
    sprintf(baseFile, "%s_3D", prefix);
    WriteSRFMask(fSRF, baseFile, width, height);

    int nOne = 1;
    fwrite(&nOne, 1, 4, fSRF);
    nOne = width * height * 36 * 2;
    fwrite(&nOne, 1, 4, fSRF);

    WriteSRFImage(fSRF, baseFile, width, height);

    sprintf(fn, "%s_Top_00.png", prefix);
    lodepng::decode(image, width, height, fn);

    *(short*)&maskHDR[12] = (short)(height);
    *(short*)&maskHDR[14] = (short)(width * 36);
    *(short*)&maskHDR[18] = (short)(width * 36 * 2);
    *(unsigned int*)&maskHDR[28] = (unsigned int)(width * height * 36);

    fwrite(&maskHDR, 1, sizeof(maskHDR), fSRF);

    sprintf(baseFile, "%s_Top", prefix);
    WriteSRFMask(fSRF, baseFile, width, height);

    nOne = 1;
    fwrite(&nOne, 1, 4, fSRF);
    nOne = width * height * 36 * 2;
    fwrite(&nOne, 1, 4, fSRF);

    WriteSRFImage(fSRF, baseFile, width, height);

    unsigned char filechksum = !Checksum(fSRF);
    fwrite(&filechksum, 1, 1, fSRF);
    fclose(fSRF);
}

void SRF::WriteSRFImage(FILE* fSRF, char* baseFile, unsigned int width, unsigned int height)
{
    char fn[256];
    std::vector<unsigned char> image; //the raw pixels
    unsigned char* buffer = new unsigned char[width*height*36*2];

    for(int k=0; k<36; k++)
    {
        sprintf(fn, "%s_%d0.png", baseFile, k);
        image.clear();

        lodepng::decode(image, width, height, fn);
        for(int r=0; r<(int)height; r++)
        {
            for(int c=0; c<(int)width; c++)
            {
                int red   = image[4 * width * r + 4 * c + 0];
                int green = image[4 * width * r + 4 * c + 1];
                int blue  = image[4 * width * r + 4 * c + 2];

                // keep upper 3 bits and lowest 2
                red = ((red & 0xe0) >> 3) | (red & 0x03);
                red = red & 0x1f;

                green = ((green & 0xe0) >> 3) | (green & 0x03);
                green = green & 0x1f;

                blue = ((blue & 0xe0) >> 3) | (blue & 0x03);
                blue = blue & 0x1f;

                unsigned short pixel = (red << 11) | (green << 6) | (blue);
               // pixel = 0xf800;
                *(short*)&buffer[r*width*36*2+k*width*2+c*2] = (short)pixel;
            }
        }
    }

    fwrite(buffer, 1, width*height*36*2, fSRF);
    delete [] buffer;
}

void SRF::WriteSRFMask(FILE* fSRF, char* baseFile, unsigned int width, unsigned int height)
{
    char fn[256];
    std::vector<unsigned char> image; //the raw pixels
    unsigned char* buffer = new unsigned char[width*height*36];

    for(int k=0; k<36; k++)
    {
        sprintf(fn, "%s_%d0.png", baseFile, k);
        image.clear();

        lodepng::decode(image, width, height, fn);
        for(int r=0; r<(int)height; r++)
        {
            for(int c=0; c<(int)width; c++)
            {
                int alpha = image[4 * width * r + 4 * c + 3];
                int red = image[4 * width * r + 4 * c + 0];
                int green = image[4 * width * r + 4 * c + 1];
                int blue = image[4 * width * r + 4 * c + 2];
                if( alpha==0 )
                    alpha = 0x80;
                else
                    alpha = 0;
                //if( alpha == 0 )
                //    alpha = 0x80;
                //else
//if( alpha == 255 )
                //    alpha = !alpha;

                buffer[r*width*36+k*width+c] = alpha;
            }
        }
    }

    fwrite(buffer, 1, width*height*36, fSRF);
    delete [] buffer;
}

bool SRF::CheckFiles(const char* prefix)
{
    char fn[256];
    unsigned int height0, width0;
    unsigned int height, width;
    std::vector<unsigned char> image;

    sprintf(fn, "%s_3D_00.png", prefix);
    lodepng::decode(image, width0, height0, fn);

    for(int i=1; i<36; i++)
    {
        sprintf(fn, "%s_3D_%d0.png", prefix, i);
        int err = lodepng::decode(image, width, height, fn);
        if( err != 0 || width != width0 || height != height0 )
        {
            const char* errTxt = lodepng_error_text(err);
            if( err != 0 )
                sprintf(lastErr, "Lodepng error {%s}", errTxt);
            else
                sprintf(lastErr, "File size mismatch in %s - width %d, height %d -  Expecting width %d, height %d", fn, width, height, width0, height0);
            return false;
        }
    }

    sprintf(fn, "%s_Top_00.png", prefix);
    lodepng::decode(image, width0, height0, fn);

    for(int i=1; i<36; i++)
    {
        sprintf(fn, "%s_Top_%d0.png", prefix, i);
        int err = lodepng::decode(image, width, height, fn);
        if( err != 0 || width != width0 || height != height0 )
        {
            const char* errTxt = lodepng_error_text(err);
            if( err != 0 )
                sprintf(lastErr, "Lodepng error {%s}", errTxt);
            else
                sprintf(lastErr, "File size mismatch in %s - width %d, height %d -  Expecting width %d, height %d", fn, width, height, width0, height0);
            return false;
        }
    }

    return true;
}
